/**
 * @author erond
 */

package master;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 * Il thread deve implementare un metodo che consenta di fare un confronto a 2 tra file xml alla volta,
 * in particolare il "mio" gateway.xml con uno alla volta quello dei vicini.
 * Per ogni MACADDRESS trovato nel file in STATE=ACTIVABLE devo verificare un eventualme matching
 * con l'xml del vicino. Se trovo:
 *        - matching POSITIVO
 *        	- se lo stato del client in questione è ACTIVE
 *        		- confronto i relativi valori di lambda. Se
 *         			- il mio è MINORE allora devo farmi carico del job sul suo file! Quindi:
 *         				- invio al gateway (un file txt contenente?) la lista di <MACADDRESS>
 *         				  dei quali voglio assumere il controllo di servizio.
 *         				- mi metto in attesa di ricevere da parte sua un file (xml)
 *                          contenente una lista di <Mac,NomeFile,MediaTime,Port>,ovvero i client
 *                          dei quali mi assumo il controllo e che lui ha provveduto a rimuovere dai
 *                          suoi serviti. 
 *         			- il mio è MAGGIORE "semplicemente" gli invio il mio file...
 *            - se lo stato del client in questione è ACTIVABLE
 *            		- "semplicemente" gli invio il mio file...
 *        - matching NEGATIVO
 *        	- "semplicemente" gli invio il mio file...
 *        
 *        
 *        In particolare, se non trovo riscontri, o trovo qualche client ACTIVABLE,
 *        vado direttamente ad aggiungere <Mac,NomeFile,MediaTime,Port> nel mio job.xml.
 *        Se invece trovo un matching, con stato ACTIVE, confronto i lambda e nel caso di
 *        job da usurpare vado a scrivere quel MACADDRESS in before_thief.txt (o after_thief.txt).
 *        
 *        
 *      Di tutto questo devo creare il file job.xml da inviarlo allo SLAVE 
 *
 */
public class LoadBalancing extends Thread{

	private String prefix = null;
	private String fileSorgente = null;
	private String fileDestinazione = null;
	private boolean stateToAdd = false;
	private int fileID = 0;

	/**
	 * @return the fileID
	 */
	public int getFileID() {
		return fileID;
	}
	private String defaultSource="file:./media/file1.wav";

	/**
	 * @param prefix Prefisso per i nomi dei file da utilizzare. Può essere "after_" o "before_" a seconda del vicino che si sta analizzando
	 * @param fileSorgente Path completo del file sorgente
	 * @param fileDestinazione Path completo del file destinazione
	 * @param stateToAdd Parametro booleano per stabilire se aggiungere o meno lo stato al file xml
	 * @param defaultSource Nome del file audio da inviare ai client di default
	 */
	public LoadBalancing(String prefix, String fileSorgente, String fileDestinazione, boolean stateToAdd, String defaultFileName) {
		this.prefix=prefix;
		this.fileSorgente=fileSorgente;
		this.fileDestinazione=fileDestinazione;
		this.stateToAdd=stateToAdd;
		this.defaultSource="file:./media/"+defaultFileName+".wav";
	}

	/**
	 * Costruttore con file ID per scegliere il file da inviare
	 * @param prefix Prefisso per i nomi dei file da utilizzare. Può essere "after_" o "before_" a seconda del vicino che si sta analizzando
	 * @param fileSorgente Path completo del file sorgente
	 * @param fileDestinazione Path completo del file destinazione
	 * @param stateToAdd Parametro booleano per stabilire se aggiungere o meno lo stato al file xml
	 * @param fileID Eventuale ID del file audio da inviare ai client
	 * @param defaultSource Nome del file audio da inviare ai client di default
	 */
	public LoadBalancing(String prefix, String fileSorgente, String fileDestinazione, boolean stateToAdd,int fileID, String defaultFileName) {
		this.prefix=prefix;
		this.fileSorgente=fileSorgente;
		this.fileDestinazione=fileDestinazione;
		this.stateToAdd=stateToAdd;
		this.fileID=fileID;
		this.defaultSource="file:./media/"+defaultFileName+".wav";
	}

	/**
	 * Metodo che restituisce vero se la stringa a, rappresentante un intero, è minore della stringa b. Falso altrimenti.
	 * @param a Prima stringa da confrontare
	 * @param b Seconda stringa da confrontare
	 * @return true if (a<b)
	 */
	public boolean confrontoLambda(String a,String b) {	
		int lambdaA=Integer.parseInt(a);
		int lambdaB=Integer.parseInt(b);
		if (lambdaA<lambdaB) return true;
		else return false;
	}

	/**
	 * Metodo che a partire da un mac address scrive su job.xml l'intera "famiglia" xml di un client
	 * con i campi a 0.
	 * @param mac Stringa rappresentate il MACADDRESS del client in esame
	 * @param lambda Valore del Lambda da aggiungere al file xml
	 */
	public void writeToXml(String mac, String lambdaToAdd) {
		SAXBuilder builder = new SAXBuilder();
		Document document = null;
		try {
			document = builder.build(new File(fileDestinazione));
		} catch (JDOMException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} 
		/**
		 * Creo l'elemento figlio relativo al bluetooth Address passato.
		 */
		Element root = document.getRootElement(); 
		Element btClient = new Element("CLIENT"); 
		btClient.setAttribute("MAC",mac); 
		/**
		 * Creo e setto gli attibuti del figlio .
		 */	
		Element source = new Element("SOURCE"); 
		Element timeValue = new Element("TRANSMITTEDTIME"); 
		Element port = new Element("PORT");
		Element state = new Element("STATE");
		Element lambda = new Element("LAMBDA");

		lambda.setText(lambdaToAdd);
		timeValue.setText("0"); 

		source.setText(defaultSource);

		port.setText("0");
		state.setText("ACTIVABLE");

		if (stateToAdd) btClient.addContent(state);
		btClient.addContent(source); 
		btClient.addContent(timeValue);
		btClient.addContent(lambda);
		btClient.addContent(port);


		root.addContent(btClient);
		/**
		 * Scrivo il file.
		 */
		XMLOutputter outputter = new XMLOutputter(); 
		outputter.setFormat(Format.getPrettyFormat()); 
		try {
			outputter.output(document, new FileOutputStream(fileDestinazione));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Metodo che scrive sul fileDestinazione un elemento client completo di parametri rappresentante un client da usurpare al vicino
	 * @param mac MACADDRESS del client in esame
	 * @param _source Nome del file che il vicino sta inviando e che dovremo usurpare
	 * @param _transmitted Ultimo istante di tempo trasmesso
	 * @param _port Porta sulla quale era attivo lo stream
	 */
	public void stealJob(String mac,String _source,String _transmitted,String _port) {
		SAXBuilder builder = new SAXBuilder();
		Document document = null;
		try {
			document = builder.build(new File(fileDestinazione));
		} catch (JDOMException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} 
		/**
		 * Creo l'elemento figlio relativo al bluetooth Address passato.
		 */
		Element root = document.getRootElement(); 
		Element btClient = new Element("CLIENT"); 
		btClient.setAttribute("MAC",mac); 
		/**
		 * Creo e setto gli attibuti del figlio .
		 */	
		Element source = new Element("SOURCE"); 
		Element timeValue = new Element("TRANSMITTEDTIME"); 
		Element port = new Element("PORT");

		timeValue.setText(_transmitted); 
		source.setText(_source);
		port.setText(_port);
		btClient.addContent(source); 
		btClient.addContent(timeValue);
		btClient.addContent(port);

		root.addContent(btClient);
		/**
		 * Scrivo il file.
		 */
		XMLOutputter outputter = new XMLOutputter(); 
		outputter.setFormat(Format.getPrettyFormat()); 
		try {
			outputter.output(document, new FileOutputStream(fileDestinazione));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	public void confrontoXml(){


		/**
		 * Creo il file di testo nel quale eventualmente stampare i mac dei client da usurpare dal vicino
		 */
		PrintStream file = null;
		try {
			file = new PrintStream(new FileOutputStream(prefix+"thief.txt"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		/**
		 * Creo un SaxBuiler per il parsing e costruisco un documento.
		 */
		SAXBuilder builder = new SAXBuilder(); 
		Document myDocument = null;
		Document neighbourDocument = null;
		try {
			myDocument = builder.build(new File(fileSorgente));
			neighbourDocument = builder.build(new File(prefix+"gateway.xml"));
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} 

		Element myRoot = myDocument.getRootElement(); 
		List<Element> myChildren = myRoot.getChildren(); 
		Iterator<Element> myIterator = myChildren.iterator(); 

		/*
		 * Confronto ogni figlio con quello che corrisponde al mac dato, e ne aggiorno i dati.
		 */
		int j=0;
		boolean toAdd = false;
		boolean toSteal = false;
		boolean toIgnore = true;

		while(myIterator.hasNext()) { 
			j++;
			Element myItem = (Element)myIterator.next(); 
			String myMac = myItem.getAttributeValue("MAC");
			System.out.println("Letto "+ myMac +" n."+j);
			toAdd=false;
			toSteal=false;
			toIgnore=false;


			/**
			 * Per ogni macaddress del MIO xml, lo confronto con tutti i mac dell'xml del vicino
			 */
			Element neighbourRoot = neighbourDocument.getRootElement(); 
			List<Element> neighbourChildren = neighbourRoot.getChildren(); 
			Iterator<Element> neighbourIterator = neighbourChildren.iterator();

			String myState = myItem.getChildText("STATE");
			String myLambda = myItem.getChildText("LAMBDA");
			if (myState.equalsIgnoreCase("ACTIVABLE")) {
				while(neighbourIterator.hasNext()) {
					Element neighbourItem = (Element)neighbourIterator.next();
					String neighbourMac =  neighbourItem.getAttributeValue("MAC");
					String neighbourSource = neighbourItem.getChildText("SOURCE");
					if (myMac.equalsIgnoreCase(neighbourMac)) {
						System.out.println("Trovo corrispondenza tra myMac: "+myMac + " e Vicino "+prefix+" "+neighbourMac);
						//trovata corrispondenza
						String neighbourState = neighbourItem.getChildText("STATE");
						//controllo se lo stato è ACTIVE, altrimenti non mi interessa
						if (neighbourState.equalsIgnoreCase("ACTIVE")) {
							String neighbourLambda = neighbourItem.getChildText("LAMBDA");
							//controllo se il mio lambda è minore dell'altro
							if (confrontoLambda(myLambda,neighbourLambda)) {
								/**
								 * il mio lambda è minore, quindi devo usurpare il job.
								 */
								System.err.println("il mio ("+myMac+") lambda = "+myLambda+" < lambda di "+neighbourMac+" = "+neighbourLambda+" , quindi devo usurpare il job!");				
								toSteal=true;
								break;
							}

							if (defaultSource.equalsIgnoreCase(neighbourSource)) {
								System.err.println("@@@@@@@ to Ignore! "+myMac);
								toIgnore=true;
								break;
							}

						}//fine corrispondenza neighbourMAC in stato ACTIVE

						if (neighbourState.equalsIgnoreCase("PENDING")) {
							if (defaultSource.equalsIgnoreCase(neighbourSource)) {
								System.err.println("@@@@@@@ to Ignore! "+myMac);
								toIgnore=true;
								break;
							}
							else {
								System.err.println(myMac+" in stato PENDING dal vicino "+prefix+" ma mySource("+defaultSource+") DIVERSA da neighbourSource("+neighbourSource+")");
							}
						}//fine corrispondenza neighbourMAC in stato PENDING

						if (neighbourState.equalsIgnoreCase("ACTIVABLE")) {
							if (defaultSource.equalsIgnoreCase(neighbourSource)) {
								System.err.println("@@@@@@@ to Ignore! "+myMac);
								toIgnore=true;
								break;
							}
							else {
								System.err.println(myMac+" in stato ACTIVABLE dal vicino "+prefix+" ma mySource("+defaultSource+") DIVERSA da neighbourSource("+neighbourSource+")");
							}
						}

					}//fine corrispondenza macaddress
				}//while(neighbourIterator.hasNext())

			}//fine corrispondenza myMAC in stato ACTIVABLE	
			else {
				System.err.println("@@@@@@@ to Ignore=true! (myMac="+myMac+" non in stato Activable, ma in stato "+myState+")!");
				toIgnore=true;
			}

			if ((toSteal==false)&&(toIgnore==false)) {
				System.out.println(myMac+": (toSteal==false)&&(toIgnore==false) quindi toAdd=true");
				toAdd=true;
			}

			if (toSteal) {
				System.err.println("******* toSteal! "+myMac);
				file.println(myMac);
			} else if (toAdd) {
				System.err.println("#######  toAdd! "+myMac);
				writeToXml(myMac, myLambda);
			}

		}//fine while(myIterator.hasNext())
	}//fine metodo confrontoXml() 

	public void run() {	
		System.out.println("Avviato LOADBALANCING con prefix="+prefix+", fileSorgente="+fileSorgente+", fileDestinazione="+fileDestinazione);
		confrontoXml();	
	}

	public static void main(String args[]) {

		LoadBalancing loadBalancingBefore = new LoadBalancing(null, "./gatewayTmp/before_", null, false, null);
		loadBalancingBefore.start();
		try {
			loadBalancingBefore.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

}
